/*
 * Copyright (C) 2010 Peter Schönhofen
 *
 * This file is part of the Akkad Compiler Framework.
 *
 * The Akkad Compiler Framework is free software: you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */

package hu.anaxo.akkad.processor

import scala.util.parsing.combinator.Parsers
import scala.util.parsing.input.Positional

/**
 * Customized parser class
 */

abstract class CustomParser extends Parsers {

  /**
   * Enhanced inner parser class
   */

  abstract class Parser[T] extends super.Parser[T] {

    /**
     * Discard result of parser
     *
     * @param parser parser
     *
     * @return parser giving an unit result
     */

    def unary_!(): UnitParser =
      new UnitParser {

        /**
         * Parse input
         *
         * @param in input
         *
         * @return result of parsing
         */

        def apply(in: Input): ParseResult[Unit] =
          Parser.this(in) match {
            case Success(_, next)  =>
              Success ((), next)
            case error: NoSuccess =>
              error
          }
      }

    /**
     * Combine regular and unit parser
     *
     * @param parser unit parser
     *
     * @return combined parser
     */

    def ~~(parser: => UnitParser): Parser[T] =
      (for (left <- this; right <- parser) yield left).named("~~")

    /**
     * Combine two regular parsers
     *
     * @param parser regular parser
     *
     * @return combined parser
     */

    def ~~[U, P <% Parser[U]](parser: => P): Parser[~[T, U]] =
      (for (left <- this; right <- parser) yield new ~(left, right)).named("~~")
  }

  /**
   * Enhanced inner parser class representing unit parsers
   */

  abstract class UnitParser extends super.Parser[Unit] {

    /**
     * Combine unit and regular parser
     *
     * @param parser regular parser
     *
     * @return combined parser
     */

    def ~~[T](parser: Parser[T]): Parser[T] =
      (for (left <- this; right <- parser) yield right).named("~~")

    /**
     * Combine two unit parsers
     *
     * @param parser unit parser
     *
     * @return combined parser
     */

    def ~~(parser: UnitParser): UnitParser =
      new UnitParser {

        /**
         * Parse input
         *
         * @param in input
         *
         * @return result of parsing
         */

        def apply(in: Input): ParseResult[Unit] =
          UnitParser.this(in) match {
            case Success(_, leftNext) =>
              parser(leftNext) match {
                case Success(_, rightNext) =>
                  Success((), rightNext)
                case error: NoSuccess =>
                  error
              }
            case error: NoSuccess =>
              error
          }
      }.named("~~")
  }

  /**
   * Conversion from regular to custom parser
   *
   * @param parser regular parser
   *
   * @return enhanced parser
   */

  implicit def toCustomParser[T](parser: super.Parser[T]): Parser[T] =
    new Parser[T] {

      /**
       * Parse input
       *
       * @param in input
       *
       * @return result of parsing
       */

      def apply(in: Input): ParseResult[T] =
        parser(in) match {
          case node @ Success(result, next) =>
            if (result.isInstanceOf[Positional])
              result.asInstanceOf[Positional].setPos(in.pos)
            node
          case error: NoSuccess =>
            error
        }
    }

  /**
   * Create parser matching given element
   *
   * @param item element to match
   *
   * @return parser
   */

  override implicit def accept(item: Elem): Parser[Elem] =
    acceptIf(_ == item) (item.toString + " expected but " + _ + " found")

  // token is the parsing unit

  type Elem = Token

  // root parser

  protected def root: Parser[Syntax]

  /**
   * Build AST node from parse result
   *
   * @param constructor constructor function
   * @param result result
   *
   * @return node
   */

  def build[T, R](constructor: T => R) (value: T) =
    constructor(value)

  def build[T1, T2, R](constructor: (T1, T2) => R) (value: ~[T1, T2]) =
    value match {
      case a ~ b =>
        constructor(a, b)
    }

  def build[T1, T2, T3, R](constructor: (T1, T2, T3) => R) (value: ~[~[T1, T2], T3]) =
    value match {
      case a ~ b ~ c =>
        constructor(a, b, c)
    }

  def build[T1, T2, T3, T4, R](constructor: (T1, T2, T3, T4) => R)
      (value: ~[~[~[T1, T2], T3], T4]) =
    value match {
      case a ~ b ~ c ~ d =>
        constructor(a, b, c, d)
    }

  def build[T1, T2, T3, T4, T5, R](constructor: (T1, T2, T3, T4, T5) => R)
      (value: ~[~[~[~[T1, T2], T3], T4], T5]) =
    value match {
      case a ~ b ~ c ~ d ~ e =>
        constructor(a, b, c, d, e)
    }

  /**
   * Perform parsing
   *
   * @param tokens tokens
   *
   * @return syntax tree
   */

  def process(tokens: List[Token]): Syntax =
    phrase(root) (new TokenReader(tokens)) match {
      case Success(result, _) =>
        result
      case error: NoSuccess =>
        throw Fault(error.msg, error.next.pos)
    }
}